package tron

import (
	"encoding/json"
	"fisherman/common/event"
	"fisherman/common/http"
	"fisherman/main/cfg"
	"fisherman/main/db"
	"fmt"
	"github.com/tidwall/gjson"
	"strconv"
	"time"
)

type HistoryUSDTTx struct {
	From   string
	To     string
	Amount float64
	TimeMS int64
}

// 查询USDT余额
func QueryUSDTBalance(address string) (balance float64) {
	data := map[string]any{
		"owner_address":     address,
		"contract_address":  USDTContractAddr,
		"function_selector": "balanceOf(address)",
		"parameter":         "0000000000000000000000" + Base58ToHex42(address),
		"visible":           true,
	}
	rsp := http.PostJsonD(cfg.GetAPI()+"/wallet/triggerconstantcontract", data)
	balanceHex := gjson.Get(rsp, "constant_result.0").String()
	_amount, _ := strconv.ParseInt(balanceHex, 16, 64)
	balance = float64(_amount) / 1e6
	return
}

// USDT转账(自动判断手续费充值手续费)。
// actionType=0时，表示正常转账，无须传dbId。
// actionType=1时，表示当前正在投食的第2、3步，会增加一些流程进度操作，必须传dbId。
// actionType=2时，表示当前正在投食的第4、5步，会增加一些流程进度操作，必须传dbId。
func TransferUSDT(fromAddr string, fromAddrKey string, toAddr string, amount float64, actionType byte, dbId int64) (txID string, log string, success bool) {
	log += fmt.Sprintf("%s发起对%s转U %f\n", fromAddr, toAddr, amount)
	if len(fromAddr) != 34 {
		log += fmt.Sprintf("TransferUSDT() fromAddr 地址格式错误：%s\n", fromAddr)
		return
	}
	if len(toAddr) != 34 {
		log += fmt.Sprintf("TransferUSDT() toAddr 地址格式错误：%s\n", toAddr)
		return
	}
	energyBalance := QueryEnergyBalance(fromAddr)
	spendEnergy, err := CheckSpendEnergy(fromAddr, toAddr, amount)
	if err != nil {
		log += fmt.Sprintf("预估能量消耗失败：%v\n", err)
		return
	}
	log += fmt.Sprintf("预估能耗：%f\n", spendEnergy)
	if energyBalance < spendEnergy {
		needEnergy := spendEnergy - energyBalance
		rentDouble := needEnergy > 65000
		costTRX := db.Cfg_rent_energy_spend_trx
		if rentDouble {
			costTRX *= 2 //对方无U消耗能量翻倍
		}
		if actionType == 1 {
			event.Emit(event.Ev_TronOperateDb, "Set_Step2_PocketRentEnergy", dbId, costTRX)
		} else if actionType == 2 {
			event.Emit(event.Ev_TronOperateDb, "Set_Step4_AddrRentEnergy", dbId, costTRX)
		}
		log += fmt.Sprintf("能量%f不足%f\n", energyBalance, spendEnergy)
		l, suc := RentEnergyFromThirdPlatform(fromAddr, fromAddrKey, float64(costTRX))
		log += l
		if !suc {
			log += "租赁能量失败\n"
			return
		}
		log += "租赁能量成功，继续转U\n"
		if actionType == 1 {
			event.Emit(event.Ev_TronOperateDb, "Set_Step2_PocketRentEnergy_Suc", dbId)
		} else if actionType == 2 {
			event.Emit(event.Ev_TronOperateDb, "Set_Step4_AddrRentEnergy_Suc", dbId, costTRX)
		}
	} else {
		log += fmt.Sprintf("能量充足：%f >= %f\n", energyBalance, spendEnergy)
		if actionType == 1 {
			event.Emit(event.Ev_TronOperateDb, "Set_Step2_PocketRentEnergy", dbId, 0)
			event.Emit(event.Ev_TronOperateDb, "Set_Step2_PocketRentEnergy_Suc", dbId)
		} else if actionType == 2 {
			event.Emit(event.Ev_TronOperateDb, "Set_Step4_AddrRentEnergy", dbId, 0)
			event.Emit(event.Ev_TronOperateDb, "Set_Step4_AddrRentEnergy_Suc", dbId, 0)
		}
	}
	if actionType == 1 {
		event.Emit(event.Ev_TronOperateDb, "Set_Step3_RechargeUSDT", dbId, 0)
	} else if actionType == 2 {
		event.Emit(event.Ev_TronOperateDb, "Set_Step5_AddrFeed", dbId, 0)
	}
	txID, l, suc2 := transferUSDT(fromAddr, fromAddrKey, toAddr, amount)
	log += l
	if !suc2 {
		return
	}
	success = true
	uBalance := QueryUSDTBalance(toAddr)
	if actionType == 1 {
		event.Emit(event.Ev_TronOperateDb, "Set_Step3_RechargeUSDT_Suc", dbId, uBalance)
	} else if actionType == 2 {
		tBalance := QueryTRXBalance(toAddr)
		event.Emit(event.Ev_TronOperateDb, "Set_Step5_AddrFeed_Suc", dbId, uBalance, tBalance)
	}
	return
}

// 查询预估能量消耗
func CheckSpendEnergy(fromAddr, toAddr string, amount float64) (spendEnergy float64, err error) {
	data := map[string]any{
		"owner_address":     fromAddr,
		"contract_address":  USDTContractAddr,
		"function_selector": "transfer(address,uint256)",
		"parameter":         generateParameter(toAddr, amount),
		"call_value":        0,
		"visible":           true,
	}
	rsp := http.PostJsonD(cfg.GetAPI()+"/wallet/triggerconstantcontract", data)
	var rspData map[string]any
	err = json.Unmarshal([]byte(rsp), &rspData)
	if err != nil {
		return
	}
	if v, exists := rspData["energy_used"]; exists {
		spendEnergy = v.(float64)
	}
	return
}

// USDT转账
func transferUSDT(fromAddr, fromAddrKey, toAddr string, amount float64) (txID, log string, success bool) {
	//创建交易
	data := map[string]any{
		"owner_address":     fromAddr,
		"contract_address":  USDTContractAddr,
		"function_selector": "transfer(address,uint256)",
		"parameter":         generateParameter(toAddr, amount),
		"fee_limit":         30 * 1e6, //单笔手续费大概15TRX（70000能量）,对方无U需要双倍，所以设置30，不能设置再小了，否则转账失败
		"energy_limit":      150000,
		"call_value":        0,
		"visible":           true,
	}
	rsp := http.PostJsonD(cfg.GetAPI()+"/wallet/triggersmartcontract", data)

	//对交易进行签名
	signedTx, err := signTx(rsp, fromAddrKey)
	if err != nil {
		log += fmt.Sprintf("签名失败：%v\n", err)
		return
	}

	//广播交易
	if cfg.Config.RealTransfer {
		err = broadcastTx(signedTx)
		if err != nil {
			log += fmt.Sprintf("签名后广播失败：%v\n", err)
			return
		}
		//查询订单是否完全成功
		txID = signedTx["txID"].(string)
		for i := 0; i < 20; i++ {
			wait, queryErr := queryTransferResult(txID)
			if wait {
				time.Sleep(time.Millisecond * 500)
				continue
			}
			if queryErr != nil {
				err = queryErr
			}
			break
		}
	} else {
		log += "未开启真实转账，跳过广播模拟成功\n"
	}
	success = true
	return
}

// 查询历史交易
func QueryHistoryUSDTTxs(address string, fromTimestampMS int64, limit int) (hisTxs []HistoryUSDTTx) {
	//limit	整数	指定返回的最大交易数量，默认值为 20，最大值为 200。
	//order_by	字符串	按字段排序，例如 block_timestamp,desc 或 block_timestamp,asc。
	//min_timestamp	整数	交易时间范围的起始时间戳（单位：毫秒）。
	//max_timestamp	整数	交易时间范围的结束时间戳（单位：毫秒）。
	params := map[string]any{
		"only_confirmed": true,
		"limit":          limit,
		"min_timestamp":  fromTimestampMS,
	}
	rsp := http.GetP(cfg.GetAPI()+"/v1/accounts/"+address+"/transactions/trc20", params)
	//log.Infof("QueryHistoryTrans:%s", rsp)
	rspData := gjson.Parse(rsp)
	if rspData.Get("success").Bool() {
		txs := rspData.Get("data").Array()
		for _, tx := range txs {
			fromAddr := tx.Get("from").String()
			toAddr := tx.Get("to").String()
			amount := float64(tx.Get("value").Float() / 1e6)
			time := tx.Get("block_timestamp").Int()
			//log.Infof("%s -> %s %f %s", fromAddr, toAddr, amount, time)
			hisTxs = append(hisTxs, HistoryUSDTTx{
				From:   fromAddr,
				To:     toAddr,
				Amount: amount,
				TimeMS: time,
			})
		}
	}
	return
}
